//Copyright 2010 Balazs Keri, Imre Keri
//
//This file is part of FireworksCreator.
//
//FireworksCreator is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//FireworksCreator is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with FireworksCreator.  If not, see <http://www.gnu.org/licenses/>.
//
#include <global.h>
#include <QApplication>
#include <QMessageBox>
#include "document.h"
#include "primitivegroup.h"
#include "primitive.h"
//
PrimitiveGroup::PrimitiveGroup(Document *doc)
:
document(doc),
state(building)
{
   description=qApp->translate("PrimitiveGroup","do nothing");
}
PrimitiveGroup::~PrimitiveGroup()
{
   for(int i=0;i<primitives.count();++i)
   {
      delete primitives[i];
   }
}
//
void PrimitiveGroup::finalize()
{
   Q_ASSERT(state==building);
   state=after;
   document->emit_post_primitive_group(*this);
   bool success=document->add_to_history(this);
   if(!success)
   {
      QMessageBox::warning(0,qApp->translate("PrimitiveGroup", "Error"),
              qApp->translate("PrimitiveGroup","%1: Failed to add this task to the undo history. The command will now be undone. Save the document as soon as possible.")
              .arg(description));
      undo();
   }
}
//
void PrimitiveGroup::set_description(const QString &desc)
{
   Q_ASSERT(state==building);
   description=desc;
}
const QString &PrimitiveGroup::get_description() const
{
   return this->description;
}
/*UiError PrimitiveGroup::add(Primitive *prim) {
   Q_ASSERT(state == building);
   primitives.append(prim);
}*/
void PrimitiveGroup::add_exec(Primitive *prim)
{
   Q_ASSERT(state==building);
   primitives.append(prim);
   UiError err;
   if((err=prim->exec(document)).failed())
   {
      UiError err2;
      for(int i=primitives.count()-2;i>=0;--i)
      {
         if((err2=primitives[i]->undo(document)).failed())
         {
            QMessageBox::StandardButton choice=QMessageBox::warning(0,qApp->translate("PrimitiveGroup","Error"),qApp->translate(
               "PrimitiveGroup",
               "%1:\nAn error occurred: %2."
                  "While rolling back half-done changes another error occurred: %3."
                  "It should be safe to ignore, but then the document will be left in a transient state.")
                  .arg(description).arg(err).arg(err2),
               QMessageBox::Retry|QMessageBox::Ignore|QMessageBox::Abort,
               QMessageBox::Retry);
            err2.mark_as_displayed();
            if(choice==QMessageBox::Retry)
            {
               i+=1;
               continue;
            }
            else if(choice==QMessageBox::Ignore)
               continue;
            err.mark_as_displayed();
            document->emit_post_primitive_group(*this);
            throw err;
         }
      }
      QMessageBox::warning(0,qApp->translate("PrimitiveGroup","Error"),qApp->translate("PrimitiveGroup","%1: An error occurred: %2.").arg(description).arg(err));
      err.mark_as_displayed();
      document->emit_post_primitive_group(*this);
      throw err;
   }
}
UiError PrimitiveGroup::exec()
{
   Q_ASSERT(state==before);
   int i;
   UiError err,err2;
   for (i=0;i<primitives.count();++i)
   {
      if(!(err=primitives[i]->exec(document)).success())
         break;
   }
   if(i<primitives.count())
   {
      for(i-=1;i>=0;--i)
      {
         if(!(err2=primitives[i]->undo(document)).success())
         {
            QMessageBox::StandardButton choice=
            QMessageBox::warning(0,qApp->translate("PrimitiveGroup","Error"),
                    qApp->translate("PrimitiveGroup",
                          "%1:\nAn error occurred: %2."
                          "While rolling back half-done changes another error occurred: %3."
                          "It should be safe to ignore, but then the document will be left in a transient state.")
                          .arg(description).arg(err).arg(err2),
                    QMessageBox::Retry|QMessageBox::Ignore|QMessageBox::Abort,
                    QMessageBox::Retry);
            err2.mark_as_displayed();
            if (choice==QMessageBox::Retry)
            {
               i+=1;
               continue;
            }
            else if(choice==QMessageBox::Ignore)
               continue;
            err.mark_as_displayed();
            document->emit_post_primitive_group(*this);
            return err;
         }
      }
      QMessageBox::warning(0,qApp->translate("PrimitiveGroup","Error"),
              qApp->translate("PrimitiveGroup","%1:\nAn error occurred: %2. Task not executed.")
              .arg(description).arg(err));
      err.mark_as_displayed();
      document->emit_post_primitive_group(*this);
      return err;
   }
   state=after;
   document->emit_post_primitive_group(*this);
   return UiError();
}
UiError PrimitiveGroup::undo()
{
   Q_ASSERT(state==after);
   int i;
   UiError err,err2;
   for (i=primitives.count()-1;i>=0;--i)
   {
      if(!(err=primitives[i]->undo(document)).success())
         break;
   }
   if(i>=0)
   {
      for(i+=1;i<primitives.count();++i)
      {
         if(!(err2=primitives[i]->exec(document)).success())
         {
            QMessageBox::StandardButton choice=
            QMessageBox::warning(0,qApp->translate("PrimitiveGroup","Error"),
                    qApp->translate("PrimitiveGroup",
                          "%1:\nWhile undoing an error occurred: %2."
                          "While rolling back half-done changes another error occurred: %3."
                          "It should be safe to ignore, but then the document will be left in a transient state.")
                          .arg(description).arg(err).arg(err2),
                    QMessageBox::Retry|QMessageBox::Ignore|QMessageBox::Abort,
                    QMessageBox::Retry);
            err2.mark_as_displayed();
            if(choice==QMessageBox::Retry)
            {
               i-=1;
               continue;
            }
            else if(choice==QMessageBox::Ignore)
               continue;
            err.mark_as_displayed();
            document->emit_post_primitive_group(*this);
            return err;
         }
      }
      QMessageBox::warning(0,qApp->translate("PrimitiveGroup","Error"),
              qApp->translate("PrimitiveGroup", "%1:\nWhile undoing an error occurred: %2. It is not undone.")
              .arg(description).arg(err));
      err.mark_as_displayed();
      document->emit_post_primitive_group(*this);
      return err;
   }
   state=before;
   document->emit_post_primitive_group(*this);
   return UiError();
}
void PrimitiveGroup::store_data(QDataStream &p_stream) const
{
   p_stream<<qint8(state)<<description<<qint32(primitives.size());
   for(PrimitiveList::const_iterator i=primitives.begin();i!=primitives.end();++i)
      (*i)->store_data(p_stream);
}
void PrimitiveGroup::restore_data(RestoreStream &p_stream)
{
   qint8 tmp; p_stream>>tmp;
   switch(tmp)
   {
   case building:state=building;break;
   case before:state=before;break;
   case after:state=after;break;
   default:throw RestoreError(qApp->translate("PrimitiveGroup","Bad value for state in PrimitiveGroup."));
   }
   qint32 tmp2;
   p_stream>>description>>tmp2;
   if(tmp2<0)
      throw RestoreError(qApp->translate("PrimitiveGroup","PrimitiveGroup::number of primitives < 0"));
   for(qint32 i=0;i<tmp2;++i)
   {
      Primitive *new_prim=Primitive::create(p_stream);
      new_prim->restore_data(p_stream);
      primitives.append(new_prim);
   }
}
